package com.j01y.myhooks.Hooks;

import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.location.Criteria;
import android.location.GpsStatus;
import android.location.GpsStatus.Listener;
import android.location.GpsStatus.NmeaListener;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build.VERSION;
import android.os.Bundle;
import android.os.SystemClock;
import android.telephony.CellLocation;
import android.telephony.gsm.GsmCellLocation;

import com.j01y.myhooks.Fake.FakeLocation;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;


public class LocationHook implements IXposedHookLoadPackage {

    private final String TAG = "czg";
    private static double lat;
    private static double lon;
    private static double baseStationLongitude;
    private static double baseStationLatitude;

    public static void logD(String str) {
        XposedBridge.log(str);
    }

    public static void log(String str) {
        XposedBridge.log(str);
    }

    public static void log(Exception exc) {
        XposedBridge.log(exc);
    }

    public static void hook_method(Class<?> cls, String str, Object... objArr) {
        try {
            XposedHelpers.findAndHookMethod(cls, str, objArr);
        } catch (Exception e) {
            log(e);
        }
    }

    public static void hook_allMethod(Class<?> cls, String str, XC_MethodHook xC_MethodHook) {
        try {
            XposedBridge.hookAllMethods(cls, str, xC_MethodHook);
        } catch (Exception e) {
            log(e);
        }
    }

    public static void hook_method(String str, ClassLoader classLoader, String str2, Object... objArr) {
        try {
            XposedHelpers.findAndHookMethod(str, classLoader, str2, objArr);
        } catch (Exception e) {
            log(e);
        }
    }

    public static void hook_methods(String str, String str2, XC_MethodHook xC_MethodHook) {
        try {
            for (Method method : Class.forName(str).getDeclaredMethods()) {
                if (method.getName().equals(str2) && !Modifier.isAbstract(method.getModifiers()) && Modifier.isPublic(method.getModifiers())) {
                    XposedBridge.hookMethod(method, xC_MethodHook);
                }
            }
        } catch (Exception e) {
            log(e);
        }
    }


    public void handleLoadPackage(LoadPackageParam loadPackageParam) throws Throwable {
        lat = FakeLocation.Latitude();
        lon = FakeLocation.Longitude();
        baseStationLongitude = FakeLocation.BaseStationLatitude();
        baseStationLatitude = FakeLocation.BaseStationLatitude();

        try {
            log(loadPackageParam.packageName + "加载了包");
            hookTelephonyManager(loadPackageParam.classLoader);
            hookPhoneStateListener(loadPackageParam.classLoader);
            hookNet(loadPackageParam.classLoader);
            hookLocationManager(loadPackageParam.classLoader);
            hookLocation(loadPackageParam.classLoader);
            hookCellLocation(loadPackageParam.classLoader);
            hookSubscriptionManager(loadPackageParam.classLoader);
            hookOther(loadPackageParam.classLoader);
        } catch (Exception e) {
            log(e);
        }
    }

    public static void hookTelephonyManager(ClassLoader classLoader) {
        hook_method("android.telephony.TelephonyManager", classLoader, "getCellLocation", new XC_MethodHook() {
            public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                XposedBridge.log("getCellLocation status=on ");
                methodHookParam.setResult(null);
            }
        });
        if (VERSION.SDK_INT > 22) {
            hook_method("android.telephony.TelephonyManager", classLoader, "getPhoneCount", new XC_MethodHook() {

                public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                    methodHookParam.setResult(1);
                }
            });
        }
        if (VERSION.SDK_INT <= 23) {
            hook_method("android.telephony.TelephonyManager", classLoader, "getNeighboringCellInfo", new XC_MethodHook() {

                public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                    methodHookParam.setResult(new ArrayList<>());
                }
            });
        }
        XposedHelpers.findAndHookMethod("android.telephony.TelephonyManager", classLoader, "getNetworkType", XC_MethodReplacement.returnConstant(1));
        XposedHelpers.findAndHookMethod("android.telephony.TelephonyManager", classLoader, "getPhoneType", XC_MethodReplacement.returnConstant(0));
        XposedHelpers.findAndHookMethod("android.telephony.TelephonyManager", classLoader, "getCurrentPhoneType", XC_MethodReplacement.returnConstant(0));
        if (VERSION.SDK_INT > 16) {
            XposedHelpers.findAndHookMethod("android.telephony.TelephonyManager", classLoader, "getAllCellInfo", new XC_MethodHook() {

                public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                    XposedBridge.log("getAllCellInfo status=on ");
                    methodHookParam.setResult(null);
                }
            });
        }
        if (VERSION.SDK_INT >= 18) {
            XposedHelpers.findAndHookMethod("android.telephony.TelephonyManager", classLoader, "getDataState", new XC_MethodHook() {

                public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                    methodHookParam.setResult(2);
                }
            });
            XposedHelpers.findAndHookMethod("android.telephony.TelephonyManager", classLoader, "getSimState", new XC_MethodHook() {

                public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                    methodHookParam.setResult(0);
                }
            });
        }
    }

    public static void hookPhoneStateListener(ClassLoader classLoader) {
        if (VERSION.SDK_INT > 16) {
            XposedHelpers.findAndHookMethod("android.telephony.PhoneStateListener", classLoader, "onCellInfoChanged", List.class, new XC_MethodHook() {

                public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                    XposedBridge.log("onCellInfoChanged status=on ");
                    methodHookParam.setResult(null);
                }
            });
        }
        if (VERSION.SDK_INT >= 18) {
            XposedHelpers.findAndHookMethod("android.telephony.PhoneStateListener", classLoader, "onCellLocationChanged", CellLocation.class, new XC_MethodHook() {

                public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                    GsmCellLocation gsmCellLocation = new GsmCellLocation();
                    gsmCellLocation.setLacAndCid(0, 0);
                    methodHookParam.setResult(gsmCellLocation);
                }
            });
        }
    }

    public static void hookNet(ClassLoader classLoader) {
        hook_method("android.net.wifi.WifiManager", classLoader, "getScanResults", new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(new ArrayList<>());
            }
        });
        hook_method("android.net.wifi.WifiManager", classLoader, "getWifiState", new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(1);
            }
        });
        hook_method("android.net.wifi.WifiManager", classLoader, "isWifiEnabled", new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(true);
            }
        });
        hook_method("android.net.wifi.WifiInfo", classLoader, "getSSID", new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(null);
            }
        });
        hook_method("android.net.NetworkInfo", classLoader, "getTypeName", new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult("WIFI");
            }
        });
        hook_method("android.net.NetworkInfo", classLoader, "isConnectedOrConnecting", new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(true);
            }
        });
        hook_method("android.net.NetworkInfo", classLoader, "isConnected", new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(true);
            }
        });
        hook_method("android.net.NetworkInfo", classLoader, "isAvailable", new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(true);
            }
        });
    }

    public static void hookLocationManager(ClassLoader classLoader) {
        hook_method(LocationManager.class, "getLastLocation", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                try {
                    Location location = new Location("gps");

                    location.setLatitude(lat);
                    location.setLongitude(lon);
                    location.setAccuracy(100.0f);
                    location.setTime(System.currentTimeMillis());
                    if (VERSION.SDK_INT >= 17) {
                        location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
                    }
                    methodHookParam.setResult(location);
                    LocationHook.log("修改了--getLastLocation--经纬度 latitude：" + lat + ", longitude" + lon);
                } catch (Exception e) {
                    LocationHook.log(e);
                }
            }
        });
        hook_method(LocationManager.class, "getLastKnownLocation", String.class, new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                try {
                    LocationHook.log((String) "触发--LocationManager--getLastKnownLocation");

                    Location location = new Location("gps");
                    location.setLatitude(lat);
                    location.setLongitude(lon);
                    location.setAccuracy(100.0f);
                    location.setTime(System.currentTimeMillis());
                    if (VERSION.SDK_INT >= 17) {
                        location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
                    }
                    methodHookParam.setResult(location);
                    LocationHook.log("修改了--getLastKnownLocation--经纬度 latitude：" + lat + ", longitude:" + lon);
                } catch (Exception e) {
                    LocationHook.log(e);
                }
            }
        });
        hook_allMethod(LocationManager.class, "getProviders", new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                ArrayList<String> arrayList = new ArrayList<>();
                arrayList.add("gps");
                methodHookParam.setResult(arrayList);
            }
        });
        hook_method(LocationManager.class, "getBestProvider", Criteria.class, Boolean.TYPE, new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult("gps");
            }
        });
        hook_method(LocationManager.class, "addGpsStatusListener", Listener.class, new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                if (methodHookParam.args[0] != null) {
                    XposedHelpers.callMethod(methodHookParam.args[0], "onGpsStatusChanged", new Object[]{1});
                    XposedHelpers.callMethod(methodHookParam.args[0], "onGpsStatusChanged", new Object[]{3});
                }
            }
        });
        hook_method(LocationManager.class, "addNmeaListener", NmeaListener.class, new XC_MethodHook() {
            public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(false);
            }
        });
        hook_method("android.location.LocationManager", classLoader, "getGpsStatus", GpsStatus.class, new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                Method method;
                GpsStatus gpsStatus = (GpsStatus) methodHookParam.getResult();
                if (gpsStatus != null) {
                    Method[] declaredMethods = GpsStatus.class.getDeclaredMethods();
                    int length = declaredMethods.length;
                    int i = 0;
                    while (true) {
                        if (i >= length) {
                            method = null;
                            break;
                        }
                        method = declaredMethods[i];
                        if (method.getName().equals("setStatus") && method.getParameterTypes().length > 1) {
                            break;
                        }
                        i++;
                    }
                    if (method != null) {
                        method.setAccessible(true);
                        try {
                            method.invoke(gpsStatus, new Object[]{5, new int[]{1, 2, 3, 4, 5}, new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 31, 31, 31, 0});
                            methodHookParam.setResult(gpsStatus);
                        } catch (Exception e) {
                            XposedBridge.log(e);
                        }
                    }
                }
            }
        });
        hook_methods("android.location.LocationManager", "requestLocationUpdates", new XC_MethodHook() {

            @SuppressLint({"NewApi"})
            public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                Method method;
                try {
                    LocationHook.log((String) "触发--LocationManager--requestLocationUpdates");
                    if (methodHookParam.args.length >= 4 && (methodHookParam.args[3] instanceof LocationListener)) {
                        LocationListener locationListener = (LocationListener) methodHookParam.args[3];
                        Method[] declaredMethods = LocationListener.class.getDeclaredMethods();
                        int length = declaredMethods.length;
                        int i = 0;
                        while (true) {
                            if (i >= length) {
                                method = null;
                                break;
                            }
                            method = declaredMethods[i];
                            if (method.getName().equals("onLocationChanged") && !Modifier.isAbstract(method.getModifiers())) {
                                break;
                            }
                            i++;
                        }

                        Location location = new Location("gps");
                        location.setLatitude(lat);
                        location.setLongitude(lon);
                        location.setAccuracy(10.0f);
                        location.setTime(System.currentTimeMillis());
                        if (VERSION.SDK_INT >= 17) {
                            location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
                        }
                        XposedHelpers.callMethod(locationListener, "onLocationChanged", new Object[]{location});
                        if (method != null) {
                            try {
                                method.invoke(locationListener, new Object[]{location});
                            } catch (Exception e) {
                                XposedBridge.log(e);
                            }
                        }
                        LocationHook.log("触发了--requestLocationUpdates的onLocationChanged--经纬度 latitude：" + lat + ", longitude" + lon);
                    }
                } catch (Exception e2) {
                    LocationHook.log(e2);
                }
            }
        });
        hook_methods("android.location.LocationManager", "requestSingleUpdate", new XC_MethodHook() {

            public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                Method method;
                LocationHook.log((String) "触发--LocationManager--requestSingleUpdate");
                if (methodHookParam.args.length >= 3 && (methodHookParam.args[1] instanceof LocationListener)) {
                    LocationListener locationListener = (LocationListener) methodHookParam.args[3];
                    Method[] declaredMethods = LocationListener.class.getDeclaredMethods();
                    int length = declaredMethods.length;
                    int i = 0;
                    while (true) {
                        if (i >= length) {
                            method = null;
                            break;
                        }
                        method = declaredMethods[i];
                        if (method.getName().equals("onLocationChanged") && !Modifier.isAbstract(method.getModifiers())) {
                            break;
                        }
                        i++;
                    }
                    if (method != null) {
                        try {

                            Location location = new Location("gps");
                            location.setLatitude(lat);
                            location.setLongitude(lon);
                            location.setAccuracy(100.0f);
                            location.setTime(System.currentTimeMillis());
                            if (VERSION.SDK_INT >= 17) {
                                location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
                            }
                            method.invoke(locationListener, new Object[]{location});
                            LocationHook.log("触发了--requestSingleUpdate的onLocationChanged--经纬度 latitude：" + lat + ", longitude:" + lon);
                        } catch (Exception e) {
                            XposedBridge.log(e);
                        }
                    }
                }
            }
        });
    }

    public static void hookLocation(ClassLoader classLoader) {
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "hasAccuracy", XC_MethodReplacement.returnConstant(true));
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "hasAltitude", XC_MethodReplacement.returnConstant(true));
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "hasBearing", XC_MethodReplacement.returnConstant(true));
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "hasSpeed", XC_MethodReplacement.returnConstant(true));
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "getExtras", new XC_MethodHook() {

            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                Bundle bundle = new Bundle();
                bundle.putInt("satellites", 12);
                methodHookParam.setResult(bundle);
            }
        });
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "getSpeed", XC_MethodReplacement.returnConstant(5.0f));
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "getAccuracy", XC_MethodReplacement.returnConstant(50.0f));
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "getBearing", XC_MethodReplacement.returnConstant(50.0f));
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "getAltitude", XC_MethodReplacement.returnConstant(50.0d));
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "getLatitude", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(lat);
            }
        });
        XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "getLongitude", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(lon);
            }
        });
        if (VERSION.SDK_INT >= 18) {
            XposedHelpers.findAndHookMethod("android.location.Location", classLoader, "isFromMockProvider", new XC_MethodHook() {
                public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                    methodHookParam.setResult(false);
                }
            });
        }
    }

    public static void hookCellLocation(ClassLoader classLoader) {
        XposedHelpers.findAndHookMethod("android.telephony.gsm.GsmCellLocation", classLoader, "getLac", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) {
                LocationHook.log("getLac原值:" + methodHookParam.getResult());
                methodHookParam.setResult(-1);
            }
        });
        XposedHelpers.findAndHookMethod("android.telephony.gsm.GsmCellLocation", classLoader, "getCid", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) {
                LocationHook.log("getCid原值:" + methodHookParam.getResult());
                methodHookParam.setResult(-1);
            }
        });
        XposedHelpers.findAndHookMethod("android.telephony.cdma.CdmaCellLocation", classLoader, "getNetworkId", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) {
                LocationHook.log("getNetworkId原值:" + methodHookParam.getResult());
                methodHookParam.setResult(-1);
            }
        });
        XposedHelpers.findAndHookMethod("android.telephony.cdma.CdmaCellLocation", classLoader, "getBaseStationId", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) {
                LocationHook.log("getCdmaCellLocation原值:" + methodHookParam.getResult());
                methodHookParam.setResult(-1);
            }
        });
        XposedHelpers.findAndHookMethod("android.telephony.cdma.CdmaCellLocation", classLoader, "getSystemId", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) {
                LocationHook.log("getSystemId原值:" + methodHookParam.getResult());
                methodHookParam.setResult(-1);
            }
        });
        XposedHelpers.findAndHookMethod("android.telephony.cdma.CdmaCellLocation", classLoader, "getBaseStationLatitude", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) {
                LocationHook.log("getBaseStationLatitude原值:" + methodHookParam.getResult());
                methodHookParam.setResult(baseStationLatitude);

            }
        });
        XposedHelpers.findAndHookMethod("android.telephony.cdma.CdmaCellLocation", classLoader, "getBaseStationLongitude", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) {
                LocationHook.log("getBaseStationLongitude原值:" + methodHookParam.getResult());
                methodHookParam.setResult(baseStationLongitude);
            }
        });
    }

    public static void hookSubscriptionManager(ClassLoader classLoader) {
        if (VERSION.SDK_INT > 21) {
            XposedHelpers.findAndHookMethod("android.telephony.SubscriptionManager", classLoader, "getActiveSubscriptionInfoCountMax", new XC_MethodHook() {
                public void afterHookedMethod(MethodHookParam methodHookParam) {
                    methodHookParam.setResult(1);
                }
            });
            XposedHelpers.findAndHookMethod("android.telephony.SubscriptionInfo", classLoader, "getCarrierName", new XC_MethodHook() {
                public void afterHookedMethod(MethodHookParam methodHookParam) {
                    methodHookParam.setResult("czg");
                }
            });
            XposedHelpers.findAndHookMethod("android.telephony.SubscriptionInfo", classLoader, "getCountryIso", new XC_MethodHook() {
                public void afterHookedMethod(MethodHookParam methodHookParam) {
                    methodHookParam.setResult("oc");
                }
            });
            XposedHelpers.findAndHookMethod("android.telephony.SubscriptionInfo", classLoader, "getDisplayName", new XC_MethodHook() {
                public void afterHookedMethod(MethodHookParam methodHookParam) {
                    methodHookParam.setResult("czg");
                }
            });
            XposedHelpers.findAndHookMethod("android.telephony.SubscriptionInfo", classLoader, "getMcc", new XC_MethodHook() {
                public void afterHookedMethod(MethodHookParam methodHookParam) {
                    methodHookParam.setResult(460);
                }
            });
            XposedHelpers.findAndHookMethod("android.telephony.SubscriptionInfo", classLoader, "getMnc", new XC_MethodHook() {
                public void afterHookedMethod(MethodHookParam methodHookParam) {
                    methodHookParam.setResult(460);
                }
            });
        }
    }

    public static void hookOther(ClassLoader classLoader) {
        hook_method("android.telephony.CellInfo", classLoader, "isRegistered", new XC_MethodHook() {
            public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                methodHookParam.setResult(true);
            }
        });
        XposedHelpers.findAndHookMethod("android.location.GpsStatus", classLoader, "getTimeToFirstFix", XC_MethodReplacement.returnConstant(1080));
        XposedHelpers.findAndHookMethod("android.provider.Settings.Secure", classLoader, "getString", ContentResolver.class, String.class, new XC_MethodHook() {
            public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                if (((String) methodHookParam.args[1]).equals("mock_location")) {
                    methodHookParam.setResult("0");
                }
            }
        });
    }
}